#pragma once

#include "PlayingCard.h"
#include "WizardEngine.h"

#pragma region TYPEDEFS

// Wizard uses a 60-card deck.  It's a standard playing card deck with two additional face denominations.
enum WizardValues {
	LOSE_CARD = 0,
	DEUCE, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT,
	NINE, TEN, JACK, QUEEN, KING, ACE,
	WIN_CARD,
	FACE_VALUE_COUNT
};
enum WizardSuits {NO_SUIT = -1, SPADES = 0, HEARTS, CLUBS, DIAMONDS, SUIT_COUNT, TRUMP};

// The most important prop of a trading card game is the cards themselves.
typedef PlayingCard<WizardValues, WizardSuits> WizardCard;

// Dereferencing a class name is annoying enough that I felt it worth giving it an alias.
typedef WizardCard* CardPointer;

// A deck of wizard cards is implemented as a standard array of CardPointers,
// which necessitates a double-derefence mark, or - for simplicity - this alias.
// Creating a typedef for these game units helps in abstraction to a certain extent as well.
typedef CardPointer* WizardDeck;

// An indexing type to identify which card from a hand of cards is being selected for an action.
typedef unsigned short int CardNumber;

// The sole purpose of this type is to give all WizardStates a unique identifier.
typedef unsigned short int NodeIndex;

#pragma endregion

/*	This segment is my attempt at developing a locally unique identification system for WizardStates.
	The idea is that a single, very long number is divided into pieces (adjacent bits) which
	each describe component data about the state.  The problem that has yet to be overcome
	is that a one-to-one relationship must exist.  In theory, the least ambiguous way to do that
	is to use a number that pinpoints all the cards that have been played and in what order.
	However, without reduction that number is HUGE.  For example: we know that there are 60 cards,
	so if one of those cards is to be represented by a number, that number is 8 bits long.
	(60 < 2^8, which is 64).  In the last round of the game, all cards are played, which means that
	this State can be described by 60 8-bit numbers.  That is a total of 480 bits, a.k.a 60 bytes
	or 15 long integers.

	Even if it were practical to give 60 bytes to every WizardState object *solely* for identification,
	we expect to create many such objects.  If the lookahead was "four moves" in a four-player game,
	then at the top of the tree, each player might choose from 15!/(15-4)! or 32760 outcomes.
	Multiply that by four (players), and then 60 (bytes per State), and you've consumed 7,862,400 bytes
	(7.5 MB) of memory already - again, just for identification.  States aren't just meant
	to hold one instance variable, so there'd be a similar cost for all the other data they contain,
	to say nothing of the time spent encoding and decoding these IDs.

	Ultimately, the solution should leverage the known game mechanics, plus the behavior of the decision
	algorithms, to determine what needs to be recorded.
	
	I was thinking that the identity of the individual cards need not be relative to the total deck,
	but rather, their index in the player's hand.
	Assuming a minimum of three players, a player's hand holds at most 20 cards (20 < 2^5).
	If the minimum player count is changed to four, then hands hold at most 15 cards (15 < 2^4).
	Another cost-saving detail is that an index can be thought to "shrink" as more cards are played
	out of the hand.  If you start with 15 cards and you play seven, you retain eight.  At this point,
	the index of the cards remaining to you has the range [0, (2^3)-1].

	The problem here is one of a shifting viewpoint.  The identifier has to remain the same between turns!
	If the record of the selections was to be maintained merely at the root of the decision tree,
	that root relocates with every turn (the AI whose turn comes after mine has a decision tree rooted at
	my level 1, if tree levels are indexed starting at zero).  To clarify, if I describe a decision path as
	"Card 5, Card 3, Card 8", then the next person might identify their paths as "Card 3, Card 8, Card N",
	dropping my Card 5 decision as irrelevant to the *future*.  However, these sequences are radically
	different, and do not point to the same lookup.

	On the other hand, when the viewpoint shifts, the vast majority of decisions calculated in the previous round
	become complete garbage.  Perhaps in the process of discarding the unused decisions, the identity
	of nodes can be refactored.  Perhaps even the dealt-hand information can be refactored.

	One final point to consider is that the lookahead's conclusions are really only significant at the end of tricks.
	Scores -- even potential scores -- aren't altered when a person leads a trick; merely when they take one.
	Therefore, the "window" of a decision tree can advance playerCount layers at any given time, and end with the trick.
*/
const NodeIndex BITS_FOR_CHOICE = 4;
const NodeIndex BITS_FOR_LEADER = 3;
const NodeIndex BITS_FOR_PLAYS = 8;
const NodeIndex ENCODE_CHOICE = 0;
const NodeIndex ENCODE_LEADER = BITS_FOR_CHOICE;
const NodeIndex ENCODE_PLAYS = ENCODE_LEADER + BITS_FOR_LEADER;
const NodeIndex BITS_IN_NODEINDEX = sizeof(NodeIndex) * 8;
const NodeIndex DECODE_CHOICE_L = BITS_IN_NODEINDEX - BITS_FOR_CHOICE - ENCODE_CHOICE;
const NodeIndex DECODE_LEADER_L = BITS_IN_NODEINDEX - BITS_FOR_LEADER - ENCODE_LEADER;
const NodeIndex DECODE_PLAYS_L = BITS_IN_NODEINDEX - BITS_FOR_PLAYS - ENCODE_PLAYS;
const NodeIndex DECODE_CHOICE_R = DECODE_CHOICE_L + ENCODE_CHOICE;
const NodeIndex DECODE_LEADER_R = DECODE_LEADER_L + ENCODE_LEADER;
const NodeIndex DECODE_PLAYS_R = DECODE_PLAYS_L + ENCODE_PLAYS;

/*	The nodes of a game decision tree, specific to the Wizard game mechanics.
	There is a lot more to read about GameStates in the GameState class documentation.
	Check it out.

	The CPP for WizardState is responsible for setting the superclass symbolic constants
	so that the general system laid down in GameEngine picks up on the specific
	implementation of Wizard.  These constants include:
		INITIAL - A WizardTrickState, since TrickStates initialize BidStates.
		INITIAL_PLAYER_SCORE - Zero, since Wizard players start having no points.
		NONE - Set to NULL, since we don't need the no-state to be able to do anything.
*/
class WizardState :
	public GameState
{
public:
	/*	Constructor.  The standard constructor doesn't do much of anything.
		Most of the mechanics and instance data are configured in child classes.
	*/
					WizardState() :
						nodeIndex(0) // This value will be overwritten in functions that call constructors.
					{
					} // end constructor

	/*	Deconstructor.
		Teardown involves the deallocation of as-you-need-it information common to all states in this game.
		This cleanup has been made contingent on whether the information has ever been consulted,
		though practically speaking, it would be difficult to imagine a situation where a GameState
		is produced without all of its details being immediately relevant.
	*/
					~WizardState()
					{
						if (actions)
							delete[] actions;
					} // end method

	/*	Every WizardState should be capable of deriving how many cards were played out in this round,
		even if that data doesn't have a dedicated instance variable.
		@return quantity of plays already completed in the round
	*/
	virtual
	CardNumber		countCardsPlayed()
					{
						return ((nodeIndex << DECODE_PLAYS_L) >> DECODE_PLAYS_R);
					} // end method

	/*	The trick count is logically derived from the amount of cards played and the number of people
		playing them.  The only thing that endangers that assumption could be the possibility that
		the total quantity of players drops mid-game.  If we make it a restriction on <i>this</i> game
		that any player who departs must be replaced before activities may continue, then we are 'safe'.
		@return	quantity of completed tricks; range = [0, roundNumber].
	*/
	CardNumber		countTricksPlayed()
					{
						return countCardsPlayed() / playerCount;
					} // end method

	/*	Inherited.  Rather than duplicate the player roster, which is potentially a tad volatile,
		I've chosen to ask the GameEngine to convert the index of the player currently taking a turn
		into a pointer to a player object.
	*/
	virtual
	GamePlayer*		getActivePlayer()
					{
						return WizardEngine::instance().identifyParticipant(getActiveSeat());
					} // end GameState method

	/*	Identify the person currently taking their turn by their seating location.
		@return	index of turn-taking player, range = [0, playerCount-1]
	*/
	SeatNumber		getActiveSeat()
					{
						return (getLeader() + countCardsPlayed()) % playerCount;
					} // end method

	/*	Static variable accessor: deck.
		It's overridden in WizardTrickState to provide the instance variables
		that include cards-played information.
		@return	the initial deal when called from this context
	*/
	virtual
	WizardDeck		getDeck() {
						return deck;
					} // end method

	/*	The 'Leader' is initialized to one position clockwise from the dealer
		at the start of a round and always represents the first person
		to play a card in the current trick.  It will change to equal
		the index of whoever gained the last won trick.
		@return	clockwise index of the person who played the first card of the trick
				range: [0, playerCount-1]
	*/
	virtual
	SeatNumber		getLeader()
					{
						return ((nodeIndex << DECODE_LEADER_L) >> DECODE_LEADER_R);
					} // end method

	/*	The 'Round Leader' is the first Leader in the round.
		@see getLeader()
		@return	clockwise index of the person who played the first card of the trick
				range: [0, playerCount-1]
	*/
	SeatNumber		getRoundLeader()
					{
						return NEXT_SEAT[dealingSeat];
					} // end method

	/*	A very simple index for the card trick that is currently being played out.
		Has range: [1, roundNumber], where roundNumber itself has range [1, 60/playerCount].
		@return	what was just stated
	*/
	CardNumber		getTrickNumber()
					{
						return countTricksPlayed() + 1;
					} // end method

	/*	Stub for inherited method.
		I believe this was written to enable early testing.
	*/
	virtual
	void			transitionTo(GameState*) {}

protected:
// STATICS
	// An array of player locations, which is used to cycle an index faster than modulus math permits.
	// Each element is set to the value mod(index + 1, playerCount) when a game is constructed.
	static
	SeatNumber*		NEXT_SEAT;

	// WizardStates are constrained to a single bidding round only, so it is both mechanically and memory
	// efficient to record how many tricks each player expects to win, at this level.
	static
	CardNumber*		bidsBySeat;
	
	// For the same reason, there is only one record of who it was that dealt out the cards this round.
	// This is the person who reveals trump, making a decision as to what suit it is when a Wizard is revealed.
	// This is also the person who sits counterclockwise to the first-bidder and round leader (first-player).
	static
	SeatNumber		dealingSeat;

	static
	CardNumber		dealtJesterCount;

	static
	CardNumber		dealtWizardCount;
	
	// The initial deal for this round, from which all branching possibilities are cloned.
	static
	WizardDeck		deck;
	
	// The playerCount is maintained here due to frequent use.  It's easier to type than GameEngine::getPlayerCount().
	static
	SeatNumber		playerCount;

	// As mentioned above, WizardStates are limited to a single round's participation.
	// This number is constrained to the range [1, 60/playerCount].
	static
	CardNumber		roundNumber;
	
	// Just like in the physical product, the scorecard maintains the players' standings
	// between bid/trick evaluations.  In this game's implementation, the score() method
	// returns a modifier on startOfRoundScores (+gains and -losses), which will be added
	// to these records when the sequence concludes.
	static
	ExpectedValue*	startOfRoundScores;
	
	// Each round has a suit whose cards are considered superior to the cards in other suits.
	// If a trump suit is played, the corresponding card's face value is the one to beat (with another trump).
	// In the final round, all cards are dealt out to the players, so there is no card left over
	// to represent trump.  In that case, the trump variable adopts a value that doesn't appear on any card
	// (NO_TRUMP).  The consequence of that is, no suit gains superiority in the round.
	static
	WizardSuits		trump;

// NON-STATICS
	// For the sake of memory-versus-speed, this list of outgoing player choices starts out empty,
	// but becomes initialized with pointers the first time that listAvailableActions() is called.
	StateList*		actions;

	// A locally-unique identifier for the GameState that will eventually allow them to be recalled
	// from a HashMap instead of forcing their reconstruction each time they are used.
	NodeIndex		nodeIndex;

	/*
		A sorting tool that puts higher denominations at lower indexes (the 'left').
		The cards are further segregated by suit, though suit order is abritrary
		to the order that they are declared in.
		@param	a	the card that has a lower index pre-sort
		@param	b	the card that has a higher index pre-sort
		@return		true, when the pre-sort order complies with the criteria stated above
					false, when it doesn't (elements will swap places as a result)
	*/
	static
	bool			byFaceValue(
						CardPointer a,
						CardPointer b
					) {
						// 'a' is intended to be the higher-valued card.
						WizardValues aFace = a->getFaceValue();
						WizardValues bFace = b->getFaceValue();

						// If 'a' is a Wizard, then it's in the right place.
						// If 'b' is a Jester, then it's in the right place.
						if (aFace == WIN_CARD || bFace == LOSE_CARD)
							return true;

						// If 'b' is a Wizard, we've just established that 'a' isn't,
						// so clearly 'b' is higher value.
						if (bFace == WIN_CARD || aFace == LOSE_CARD)
							return false;

						WizardSuits aSuit = a->getSuit();
						WizardSuits bSuit = b->getSuit();

						// The remaining ordering is dependent on suit.
						// If the cards have the same suit,
						// their face value determines superiority.
						// If they don't have the same suit,
						// the current trump is deemed the higher-valued suit.
						// If neither card's suit is trump,
						// then the arbitrary ordering of suits in the enumeration
						// is the tiebreaker.
						return (aSuit == bSuit)
							? aFace > bFace
							: (aSuit == trump)
								? true
								: (bSuit == trump)
									? false
									: aSuit > bSuit
						;
					} // end class method

	CardNumber		countCards()
	{
		return selectedEnd - selectedStart;
	} // end method

	CardNumber		countCards(const WizardValues withFaceValue)
					{
						auto count = 0;
						auto end = selectedEnd;

						while (selectedStart != end--)
							if (withFaceValue == (*end)->getFaceValue())
								++count;

						return count;
					} // end method

	CardNumber		countJesters()
					{
						return countCards(WizardValues::LOSE_CARD);
					} // end method

	CardNumber		countWizards()
					{
						return countCards(WizardValues::WIN_CARD);
					} // end method

	/*	Sort every player's hand so that the highest values are on the "left".
		This preps the hands for the evaluation that score() does.
	*/
	void			dealHands()
					{
						WizardCard::shuffle(deck);

						selectReserves();

						// Organize all the cards that were not distributed to players.
						std::sort(selectedStart, selectedEnd, byFaceValue);

						dealtJesterCount = WizardSuits::SUIT_COUNT - countJesters();
						dealtWizardCount = WizardSuits::SUIT_COUNT - countWizards();
					} // end method

	/*
		In Wizard, tricks are sets of N cards where N is the number of players.
		Each player contributes one card to this trick, in the order that they are seated,
		starting with the %leader%.

		The first non-Jester card played determines the suit of the trick.
		If only Jesters are played -- an outcome only possible in games with < 5 players
		-- then the leader of the trick wins the trick and remains leader for the next trick.

		When "trump is played", meaning a card with a suit that was designated as trump in the bidding round,
		the suit of the trick immediately changes to the trump suit.

		If any player plays a card with the Wizard *denomination*, the first player that does so
		wins the trick regardless of any other rule.  Otherwise,

		The winner of the trick is the player who owns the card with the highest face value
		aligned to the trick suit.

		@param whichTrick	the index of the trick in which the card was played; indexing starts at 1
		@param leader		the seat index of the player whose card we wish to identify; indexing starts at 0
		@param hands		deck of CardPointers; it's dereferenced twice to get at a WizardCard structure
		@return				the seat index of the player who won the trick
	*/
	SeatNumber		determineWinner(
						const CardNumber	whichTrick,
						SeatNumber			leader
					) {
						const SeatNumber FIRST_SEAT_INSPECTED = leader;
						bool firstEvaluation = true;

						WizardValues winningFace = LOSE_CARD;
						WizardSuits winningSuit = NO_SUIT;
						SeatNumber winningSeat = leader;

						do {
							selectPlayer(leader);
							CardPointer& playedCard = pickPlayedCard(whichTrick);
							const auto f = playedCard->getFaceValue();
							const auto s = playedCard->getSuit();

							// Wizards win immediately when encountered in play order.
							if (WIN_CARD == f)
								return leader;

							if (s != winningSuit)
							{
								if (firstEvaluation || s == trump)
								{
									winningFace = f;
									winningSuit = s;
									winningSeat = leader;
									firstEvaluation = false;
								} // end if: Player took lead with the first valued card, or trump.

								// A failure to follow suit results in a short-circuit on face value evaluation,
								// regardless of any other outcome.
								continue;
							} // end if: Player didn't follow suit.
								
							// Player takes lead with card of higher denomination in suit.
							if (winningFace < f)
							{
								winningFace = f;
								winningSuit = s;
								winningSeat = leader;
							}
						} while (FIRST_SEAT_INSPECTED != (leader = NEXT_SEAT[leader])); // cycle the seat of focus

						return winningSeat;
					} // end method

	CardNumber		estimateBid(const SeatNumber bidder)
					{
						CardNumber alreadyWon = 0;

						{	// Before we are capable of estimating the amount of tricks yet to be won,
							// we must determine how many have been won.
							auto leader					= getRoundLeader();
							const
							auto COMPLETED_TRICK_COUNT	= countTricksPlayed();
							for (
								CardNumber whichTrick	= 0;
								COMPLETED_TRICK_COUNT	!= whichTrick++;
							)
								if (bidder == (leader = determineWinner(whichTrick, leader)))
									++alreadyWon;
						} // end subroutine: count tricks bidder has won

						selectHand(bidder);
						std::sort(selectedStart, selectedEnd, byFaceValue);

						CardNumber certainWinnerCount = countWizards();
						CardNumber certainLoserCount = countJesters();
						CardNumber neutralCount = countCards();
						CardNumber likelyWinnerCount = 0;
						CardNumber likelyLoserCount = 0;
						CardNumber highIndex = certainWinnerCount;
						CardNumber lowIndex = neutralCount - certainLoserCount;

						WizardSuits currentSuit = WizardSuits::NO_SUIT;
						WizardValues consecutiveDenomination;

						do {
							CardPointer& unplayedCard = pickUnplayedCard(highIndex);
						} while (++highIndex != lowIndex)
					}

	inline
	CardPointer&	pickUnplayedCard(CardNumber cardIndex)
	{
		return selectedStart[cardIndex];
	} // end method

	/*
		The deck is modelled so that each hand is contiguous,
		each player is ordered by their seat index,
		and the cards played have been moved to the 'right' side of the hand
		in the order they were played.  (Rightmost = first played.)

		This means that we look to the card that is "X tricks back"
		from the player whose turn follows the player of interest.
		(The following player may or may not exceed the space allocated to the deck
		- but that won't matter, since we address before it, not within it.)

		@param trickIndex	the index of the trick in which the card was played; indexing starts at 1
		@param player		the seat index of the player whose card we wish to identify; indexing starts at 0
		@param hands		deck of CardPointers; it's dereferenced twice to get at a WizardCard structure
		@return				the data object that represents the card %player% played in Trick %whichTrick%.
	*/
	inline
	CardPointer&	pickPlayedCard(CardNumber trickIndex)
	{
		return selectedEnd[-trickIndex];
	} // end method

	inline
	CardPointer&	pickTurnup()
	{
		return getDeck()[roundNumber * playerCount];
	} // end method

	/*
		A card is said to be played when it is moved from cardIndex
		to the "back" of the hand, where the "back" is defined as the index
		roundNumber - trickIndex cards away from the left-most card in hand.

		The following illustration of the process uses P to represent the played card,
		T to represent cards already played in past tricks,
		S to represent the card that is displaced by the play,
		and X to represent an unplayed card:

		BEFORE
			XXXXTTT XXXXTTT XPXXSTT XXXXXTT

		AFTER
			XXXXTTT XXXXTTT XSXXPTT XXXXXTT

		@param seat			location of the person playing the card [range: 0 to playerCount-1]
		@param trickIndex	which trick number is this play occuring in?
								[This number begins at 1 and could be determined by getTrickNumber(),
								but is passed as a parameter instead to reduce the number of operations.]
		@param cardIndex	location in the hand of the card being played
								[0 is 'leftmost'.  Number is not validated, but cannot exceed (roundNumber - trickIndex).]
	*/
	inline
	void			playCard(
						const CardNumber	cardIndex,
						const CardNumber	trickIndex
					) {
						std::swap(pickUnplayedCard(cardIndex), pickPlayedCard(trickIndex));
					} // end method

	void			selectHand(SeatNumber seat)
	{
		selectPlayer(seat);
		selectedEnd -= countTricksPlayed();
	} // end method

	void			selectPlayer(SeatNumber seat)
					{
						selectedStart	= getDeck() + seat * roundNumber;
						selectedEnd		= selectedStart + roundNumber;
					} // end method

	void			selectReserves()
					{
						selectWholeDeck();
						selectedStart	+= roundNumber * playerCount;
					} // end method

	void			selectWholeDeck()
					{
						selectedStart	= getDeck();
						selectedEnd		= selectedStart + WizardCard::count();
					} // end method

private:
	WizardDeck		selectedStart;
	WizardDeck		selectedEnd;
}; // end class
